package dao

import (
	"context"
	"fmt"
	"github.com/google/uuid"
	"gorm.io/gorm"
)

type AccidentDAO struct {
	db *gorm.DB
}

func NewAccidentDap(db *gorm.DB) *AccidentDAO {
	return &AccidentDAO{
		db: db,
	}
}

func (dao *AccidentDAO) Create(ctx context.Context, e Accident) (int64, error) {
	db := dao.db
	accidentUUid := uuid.New().String()

	// insert experiment
	accident := Accident{
		Id:            accidentUUid,
		Title:         e.Title,
		TitleEn:       e.TitleEn,
		Year:          e.Year,
		Location:      e.Location,
		Dead:          e.Dead,
		Injure:        e.Injure,
		Degree:        e.Degree,
		Season:        e.Season,
		Description:   e.Description,
		DescriptionEn: e.DescriptionEn,
	}
	result := db.Table("accident").Create(&accident)
	if result.RowsAffected < 1 {
		panic(result.Error)
	}
	var ChemicalAccidents []ChemicalAccident
	var EquipmentAccidents []EquipmentAccident
	var ConsumableAccidents []ConsumableAccident

	// todo: 物品类型硬编码 后续应改为代码集
	if len(e.Chemicals) != 0 {
		for _, v := range e.Chemicals {
			ChemicalAccidents = append(ChemicalAccidents, ChemicalAccident{
				Id:         uuid.New().String(),
				AccidentId: accidentUUid,
				ChemicalId: v,
			})
		}
		err := db.Table("chemical_accident").Create(&ChemicalAccidents).Error
		if err != nil {
			panic(err)
		}
	}
	if len(e.Equipments) != 0 {

		for _, v := range e.Equipments {
			EquipmentAccidents = append(EquipmentAccidents, EquipmentAccident{
				Id:          uuid.New().String(),
				AccidentId:  accidentUUid,
				EquipmentId: v,
			})
		}
		err := db.Table("equipment_accident").Create(&EquipmentAccidents).Error
		if err != nil {
			panic(err)
		}
	}
	if len(e.Consumables) != 0 {
		for _, v := range e.Consumables {
			ConsumableAccidents = append(ConsumableAccidents, ConsumableAccident{
				Id:           uuid.New().String(),
				AccidentId:   accidentUUid,
				ConsumableId: v,
			})
		}
		// insert chemical
		err := db.Table("consumable_accident").Create(&ConsumableAccidents).Error
		if err != nil {
			panic(err)
		}
	}

	return result.RowsAffected, nil
}

func (dao *AccidentDAO) Query(ctx context.Context, a AccidentSearchParam) ([]Accident, error) {
	var accidents []Accident
	var accidentIds []string
	var accidentIds1 []string
	var accidentIds2 []string
	var accidentIds3 []string
	db := dao.db

	// 定义查询参数
	chemicalID := a.ChemicalId
	equipmentID := a.EquipmentId
	consumableID := a.ConsumableId

	// 构建子查询
	if chemicalID != "" {
		err := db.Table("chemical_accident t1").
			Select("t1.accident_id").
			Where("t1.chemical_id = ?", chemicalID).Find(&accidentIds1).Error
		if err != nil {
			panic(err)
		}
		if len(accidentIds1) == 0 {
			return accidents, nil
		}
		accidentIds = append(accidentIds, accidentIds1...)
	}
	if equipmentID != "" {
		err := db.Table("equipment_accident t2").
			Select("t2.accident_id").
			Where("t2.equipment_id = ?", equipmentID).Find(&accidentIds2).Error
		if err != nil {
			panic(err)
		}
		if len(accidentIds2) == 0 {
			return accidents, nil
		}
		accidentIds = append(accidentIds, accidentIds2...)
	}

	if consumableID != "" {
		err := db.Table("consumable_accident t3").
			Select("t3.accident_id").
			Where("t3.consumable_id = ?", consumableID).Find(&accidentIds3).Error
		if err != nil {
			panic(err)
		}
		if len(accidentIds3) == 0 {
			return accidents, nil
		}
		accidentIds = append(accidentIds, accidentIds3...)
	}
	if len(accidentIds) != 0 {
		db = db.WithContext(ctx).Where("id IN ?", accidentIds)
	}

	if title := a.Title; title != "" {
		title = fmt.Sprintf("%%%v%%", title)
		db = db.WithContext(ctx).Where("title like ?", title)
	}

	if season := a.Season; season != "" {
		season = fmt.Sprintf("%%%v%%", season)
		db = db.WithContext(ctx).Where("season like ?", season)
	}

	err := db.Debug().
		//Where("id IN ?", accidentIDs).
		//Where("title LIKE ?", titlePattern).
		//Where("season = ?", season).
		Find(&accidents).Error
	//err := db.Debug().WithContext(ctx).Find(&accidents).Error
	if err != nil {
		fmt.Println("query error")
	}
	return accidents, nil
}

func (dao *AccidentDAO) Delete(ctx context.Context, ids []string) (int64, error) {
	db := dao.db

	if len(ids) == 0 {
		fmt.Println("params error")
		return 0, nil
	}

	err := db.Table("accident").Debug().WithContext(ctx).Delete(&Accident{}, ids)
	if err != nil {
		fmt.Println("delete error")
	}

	// todo: delete related experiment items
	err = db.Table("chemical_accident").Debug().WithContext(ctx).Where("accident_id in ?", ids).Delete(&ChemicalAccident{})
	if err != nil {
		fmt.Println("delete related chemical items  error")
	}

	err = db.Table("equipment_accident").Debug().WithContext(ctx).Where("accident_id in ?", ids).Delete(&EquipmentAccident{})
	if err != nil {
		fmt.Println("delete related equipment items  error")
	}

	err = db.Table("consumable_accident").Debug().WithContext(ctx).Where("accident_id in ?", ids).Delete(&ConsumableAccident{})
	if err != nil {
		fmt.Println("delete related consumable items  error")
	}
	return err.RowsAffected, nil
}

func (dao *AccidentDAO) QueryRelatedItem(ctx context.Context, AccidentId string) (Items, error) {
	var ChemicalItems []string
	var EquipmentItems []string
	var ConsumableItems []string
	db := dao.db

	if AccidentId != "" {
		//condition := fmt.Sprintf("%%%v%%", id)
		db = db.WithContext(ctx).Where("t1.accident_id = ?", AccidentId)
	}
	//if a.Name == "" {
	//	return []Experiment{}, nil
	//}
	//err := db.Table("").Debug().WithContext(ctx).Find(&Questions).Error
	err := db.Table("chemical_accident t1").Debug().
		Select("concat_ws(' -- ', chemical.name, chemical.dict_name)").
		Joins("join chemical on chemical.id = t1.chemical_id").
		//Where("t1.accident_id = ?", AccidentId).
		Scan(&ChemicalItems).Error

	err = db.Table("equipment_accident t1").Debug().
		Select("concat_ws(' -- ', equipment.name, equipment.dict_name)").
		Joins("join equipment on equipment.id = t1.equipment_id").
		//Where("t1.accident_id = ?", AccidentId).
		Scan(&EquipmentItems).Error

	err = db.Table("consumable_accident t1").Debug().
		Select("concat_ws(' -- ', consumable.name, consumable.dict_name)").
		Joins("join consumable on consumable.id = t1.consumable_id").
		//Where("t1.accident_id = ?", AccidentId).
		Scan(&ConsumableItems).Error

	// First query - equipment accidents

	if err != nil {
		fmt.Println("query error")
	}

	return Items{
		Chemicals:   ChemicalItems,
		Equipments:  EquipmentItems,
		Consumables: ConsumableItems,
	}, nil
}

func (dao *AccidentDAO) UpdateRelatedItem(ctx context.Context, accidentId string, itemType string, items []string) (int64, error) {
	db := dao.db

	if accidentId == "" {
		//condition := fmt.Sprintf("%%%v%%", id)
		//db = db.WithContext(ctx).Where("t2.accident_id = ?", accidentId)
		return 0, nil
	}
	if itemType == "1" {
		var insertData []ChemicalAccident
		// delete related items
		err := db.Table("chemical_accident").Debug().Where("accident_id = ?", accidentId).Delete(&ChemicalAccident{}).Error
		if err != nil {
			panic(err)
			return 0, nil
		}
		// insert related items
		for _, v := range items {
			var id = uuid.New().String()
			insertData = append(insertData, ChemicalAccident{
				Id:         id,
				AccidentId: accidentId,
				ChemicalId: v,
			})
		}
		rowsAffected := db.Table("chemical_accident").Debug().Create(&insertData).RowsAffected
		return rowsAffected, nil
	}
	if itemType == "2" {
		var insertData []EquipmentAccident
		err := db.Table("equipment_accident").Debug().Where("accident_id = ?", accidentId).Delete(&EquipmentAccident{}).Error
		if err != nil {
			panic(err)
			return 0, nil
		}
		for _, v := range items {
			var id = uuid.New().String()
			insertData = append(insertData, EquipmentAccident{
				Id:          id,
				AccidentId:  accidentId,
				EquipmentId: v,
			})
		}
		rowsAffected := db.Table("equipment_accident").Debug().Create(&insertData).RowsAffected

		return rowsAffected, nil
	}
	if itemType == "3" {
		var insertData []ConsumableAccident
		err := db.Table("consumable_accident").Debug().Where("accident_id = ?", accidentId).Delete(&ConsumableAccident{}).Error
		if err != nil {
			panic(err)
			return 0, nil
		}
		for _, v := range items {
			var id = uuid.New().String()
			insertData = append(insertData, ConsumableAccident{
				Id:           id,
				AccidentId:   accidentId,
				ConsumableId: v,
			})
		}
		rowsAffected := db.Table("consumable_accident").Debug().Create(&insertData).RowsAffected

		return rowsAffected, nil
	}

	return 0, nil
}

func (dao *AccidentDAO) QueryItemAll(ctx context.Context, AccidentId string, ItemType string) ([]KeyValueItem, error) {
	//var ChemicalItems []KeyValueItem
	//var EquipmentItems []KeyValueItem
	//var ConsumableItems []KeyValueItem
	var kvItems []KeyValueItem
	db := dao.db

	if AccidentId != "" {
		//condition := fmt.Sprintf("%%%v%%", id)
		db = db.WithContext(ctx).Where("t2.accident_id = ?", AccidentId)
	}
	//if a.Name == "" {
	//	return []Experiment{}, nil
	//}
	//err := db.Table("").Debug().WithContext(ctx).Find(&Questions).Error
	if ItemType == "1" {
		err := db.Table("chemical t1").Debug().
			Select("concat_ws(' -- ', t1.name, t1.dict_name) AS name, t1.id AS id").
			Joins("left join chemical_accident t2 on t1.id = t2.chemical_id").
			//Where("t1.accident_id = ?", AccidentId).
			Scan(&kvItems).Error

		if err != nil {
			panic(err)
		}

		return kvItems, nil
	}
	if ItemType == "2" {
		err := db.Table("equipment t1").Debug().
			Select("concat_ws(' -- ', t1.name, t1.dict_name) AS name, t1.id AS id").
			Joins("left join equipment_accident t2 on t1.id = t2.equipment_id").
			//Where("t1.accident_id = ?", AccidentId).
			Scan(&kvItems).Error

		if err != nil {
			panic(err)
		}

		return kvItems, nil
	}
	if ItemType == "3" {
		err := db.Table("consumable t1").Debug().
			Select("concat_ws(' -- ', t1.name, t1.dict_name) AS name, t1.id AS id").
			Joins("left join consumable_accident t2 on t1.id = t2.consumable_id").
			//Where("t1.accident_id = ?", AccidentId).
			Scan(&kvItems).Error

		if err != nil {
			panic(err)
		}

		return kvItems, nil
	}

	return kvItems, nil
	// First query - equipment accidents

	//var TreeItems = make([]TreeItem, 0)
	//var ChemicalTreeItems = make([]TreeItem, 0)
	//var EquipmentTreeItems = make([]TreeItem, 0)
	//var ConsumableTreeItems = make([]TreeItem, 0)
	//for _, v := range ChemicalItems {
	//	ChemicalTreeItems = append(ChemicalTreeItems, TreeItem{
	//		Id:       v.Id,
	//		ParentId: "1",
	//		Label:    v.Name,
	//	})
	//}
	//for _, v := range EquipmentItems {
	//	EquipmentTreeItems = append(EquipmentTreeItems, TreeItem{
	//		Id:       v.Id,
	//		ParentId: "2",
	//		Label:    v.Name,
	//	})
	//}
	//for _, v := range ConsumableItems {
	//	ConsumableTreeItems = append(ConsumableTreeItems, TreeItem{
	//		Id:       v.Id,
	//		ParentId: "3",
	//		Label:    v.Name,
	//	})
	//}
	//TreeItems = append(TreeItems, TreeItem{
	//	Id:       "1",
	//	ParentId: "0",
	//	Label:    "Chemical",
	//	Children: ChemicalTreeItems,
	//})
	//TreeItems = append(TreeItems, TreeItem{
	//	Id:       "2",
	//	ParentId: "0",
	//	Label:    "equipment",
	//	Children: EquipmentTreeItems,
	//})
	//TreeItems = append(TreeItems, TreeItem{
	//	Id:       "1",
	//	ParentId: "0",
	//	Label:    "consumable",
	//	Children: ConsumableTreeItems,
	//})

	//return Items{
	//	Chemicals:   ChemicalItems,
	//	Equipments:  EquipmentItems,
	//	Consumables: ConsumableItems,
	//}, nil
	//return TreeItems, nil
}

type Accident struct {
	Id            string `gorm:"autoIncrement, primary_key"`
	Title         string
	TitleEn       string `gorm:"title_en"`
	Year          string
	Location      string
	Dead          int
	Injure        int
	Degree        int
	Season        string
	Equipments    []string `gorm:"-"`
	Chemicals     []string `gorm:"-"`
	Consumables   []string `gorm:"-"`
	Description   string
	DescriptionEn string `gorm:"description_en"`
}

type AccidentSearchParam struct {
	Title        string
	ChemicalId   string
	EquipmentId  string
	ConsumableId string
	Season       string
}

type ChemicalAccident struct {
	Id         string `gorm:"autoIncrement, primary_key"`
	AccidentId string `gorm:"accident_id"`
	ChemicalId string `gorm:"chemical_id"`
}

type EquipmentAccident struct {
	Id          string `gorm:"autoIncrement, primary_key"`
	AccidentId  string `gorm:"accident_id"`
	EquipmentId string `gorm:"equipment_id"`
}

type ConsumableAccident struct {
	Id           string `gorm:"autoIncrement, primary_key"`
	AccidentId   string `gorm:"accident_id"`
	ConsumableId string `gorm:"consumable_id"`
}

type Items struct {
	Chemicals   []string
	Equipments  []string
	Consumables []string
}

type KeyValueItem struct {
	Id   string `gorm:"id"`
	Name string `gorm:"name"`
}

type TreeItem struct {
	Id       string
	ParentId string
	Label    string
	Children []TreeItem
}
